using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using PMC.Interfaces;
using PMC.Plugins;
using PMC.Scripts;

namespace PMC.Urls
{
    /// <summary>
    /// Abstract class for collecting URLS
    /// </summary>
    public abstract class UriCollector : ScriptInfo, IEnumerable<Uri>, ISaveloadable
    {
        //Todo why not make this field static?
        private readonly SynchronizedCollection<Uri> uris = new SynchronizedCollection<Uri>();
        private object _localLock = new object();
        private static object _lock = new object();
        //private readonly SynchronizedCollection<IObserver<UriCollector>> observers = new SynchronizedCollection<IObserver<UriCollector>>();

        private static volatile UriCollector activeCollector;
        public static UriCollector ActiveCollector
        {
            get
            {
                if (activeCollector == null)
                {
                    lock (_lock)
                    {
                        if (activeCollector == null)
                        {
                            activeCollector = Plugin<UriCollector>.FindPlugin(Properties.Settings.Default.UriCollector).Instance;
                        }
                    }
                }
                return activeCollector;
            }
            set
            {
                Properties.Settings.Default.UriCollector = value.GetType().ToString();
                activeCollector = value;
                //OnUriCollectorChanged(); //Informs that a new collector has set
            }
        }

        /// <summary>
        /// Update URL collection
        /// </summary>
        public abstract void Update();

        /// <summary>
        /// Update URL collection
        /// </summary>
        public abstract Task UpdateAsync();


        /// <summary>
        /// Method for loading settings
        /// </summary>

        private int numProcessed;

        public int NumberProcessed
        {
            get { return numProcessed; }
        }

        /// <summary>
        /// Loads a list of URLs and adds them to the current collection
        /// </summary>
        /// <param name="urls"></param>
        public virtual void LoadUri(IList<Uri> urls)
        {
            foreach (var uri in urls)
            {
                if (uris.Contains(uri))
                    continue;

                uris.Add(uri);
                OnUrlAdded(uri);
            }
        }

        public virtual void Remove(Uri uri)
        {
            uris.Remove(uri);
            //OnNext();
            OnUrlDeleted(uri);
        }

        public virtual void Add(Uri uri)
        {
            if (uris.Contains(uri))
                return;

            uris.Add(uri);
            //OnNext();
            OnUrlAdded(uri);
        }

        #region Iterator
        /// <summary>
        /// Returns the number of URLs of the collection
        /// </summary>
        public int Count
        {
            get { return uris.Count; }
        }

        /// <summary>
        /// Returns next URL from the collection
        /// </summary>
        /// <returns></returns>
        public Uri Next()
        {
            if (Count > 0)
            {
                lock (_localLock) //test
                {
                    if (Count > 0)
                    {
                        var r = uris[0];
                        uris.RemoveAt(0);
                        //OnNext();
                        OnUrlDeleted(r);
                        numProcessed++;
                        return r;
                    }
                }
            }
            return null;
        }

        public IEnumerator<Uri> GetEnumerator()
        {
            return uris.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        #endregion

        public delegate void UrlEventHandler(object sender, UrlEventArgs e);

        public static event UrlEventHandler UrlAdded;
        public static event UrlEventHandler UrlDeleted;
        public static event EventHandler Completed;
        public static event ErrorEventHandler Error;
        //public static event EventHandler UriCollectorChanged;

        //public static void OnUriCollectorChanged()
        //{
        //    if (UriCollectorChanged != null)
        //    {
        //        UriCollectorChanged.Invoke(null, EventArgs.Empty);
        //    }
        //}

        public static void OnUrlAdded(Uri uri)
        {
            if (UrlAdded != null)
            {
                UrlAdded.Invoke(null, new UrlEventArgs(uri));
            }
        }

        public static void OnUrlDeleted(Uri uri)
        {
            if (UrlDeleted != null)
            {
                UrlDeleted.Invoke(null, new UrlEventArgs(uri));
            }
        }

        public static void OnCompleted()
        {
            if (Completed != null)
            {
                Completed.Invoke(null, new EventArgs());
            }
        }

        public static void OnError(Exception exception)
        {
            if (Error != null)
            {
                Error.Invoke(null, new ErrorEventArgs(exception));
            }
        }

        /// <summary>
        /// Shows settingsdialog for changing the settings
        /// </summary>
        /// <returns>On a succesfull change this must return DialogResult.OK</returns>
        public abstract DialogResult ShowSettings();

        /// <summary>
        /// Boolean representing if the settings have been changed
        /// </summary>
        public abstract bool Changed { get; protected set; }
        /// <summary>
        /// Loads settings
        /// </summary>
        public abstract void LoadSettings();
        public void Load()
        {
            LoadSettings();
            Changed = false;
        }
        /// <summary>
        /// Saves settings
        /// </summary>
        public abstract void SaveSettings();
        public void Save()
        {
            //Properties.Settings.Default.UriCollector = activeCollector.GetType().ToString();
            SaveSettings();
            Changed = false;
        }
        public abstract void Reload();

        public override string ToString()
        {
            return GetScriptInfoByCulture(CultureInfo.CurrentCulture).Title; //Todo invariant culture
        }
    }

    public class UrlEventArgs : EventArgs
    {
        public Uri Url { get; set; }

        public UrlEventArgs(Uri url)
        {
            this.Url = url;
        }
    }
}
